home *** CD-ROM | disk | FTP | other *** search
- Date: Thu, 5 Nov 1998 02:38:51 +0200
- From: Tatu Ylonen <ylo@SSH.FI>
- To: BUGTRAQ@netspace.org
- Subject: security patch for ssh-1.2.26 kerberos code
-
- -----BEGIN PGP SIGNED MESSAGE-----
-
- This message contains information relevant to people who compile ssh
- with --with-kerberos5. There is one or more potential security
- problem in the Kerberos code. These issues are not relevant for
- people who have not explicitly specified --with-kerberos5 on the
- configure command line.
-
- Peter Benie <pjb1008@cam.ac.uk> found a buffer overflow in the
- kerberos authentication code. To quote from his mail:
-
- > What about sshconnect.c, line 1139
- >
- > sprintf(server_name,"host/%s@", remotehost);
- >
- > where remotehost is (char *) get_canonical_hostname() (up to 255 chars),
- > is copied into server_name (a 128 char buffer)?
-
- It looks to me like this is a genuine buffer overflow. I had not
- noticed it when going through the code.
-
- This buffer overflow is, however, extremely hard to exploit:
- 1. The victim must have have client compiled with --with-kerberos5 and
- --enable-kerberos-tgt-passing.
- 2. The victim must be connecting to a server running with the same
- options (i.e., krb5 with tgt passing).
- 3. You must do the following DNS spoofing:
- - fake reverse map for the *server*
- - fake forward map for the fake reversed name
- 4. You must fake your attack code to look like valid DNS records; this
- is highly untrivial with modern versions of bind that reject all
- domain names with invalid characters in them.
- 5. Only the part of the DNS name beyond 128 bytes can be exploited; that
- must be made to align with stack frames and must contain appropriate
- return addresses and jump addresses. It has been shown that this can
- generally be done, but the space and structural constraints here are
- extremely tight compared to most instances of buffer overflow
- exploits.
- 6. Since the client with Kerberos TGT passing is only used
- interactively, the user will almost certainly notice that something
- went wrong. I don't think you can, within the structure and space
- constraints, construct the code so that the user would not notice at
- least the client crashing.
- 7. You cannot try again after a failed attack until the client again
- tries to log into the same host.
-
- This might yield an attack against the *client*.
-
- I've fixed this in the source tree.
-
- I'd like to thank Peter for reporting this. A fix will be included in
- the next release (which I expect in about a week).
-
- The patch for this problem is quote simple:
- - --- sshconnect.c.orig Thu Nov 5 02:09:55 1998
- +++ sshconnect.c Thu Nov 5 02:10:53 1998
- @@ -1090,7 +1090,7 @@
- krb5_data outbuf;
- krb5_error_code r;
- int type;
- - - char server_name[128];
- + char server_name[512];
-
- remotehost = (char *) get_canonical_hostname();
- memset(&outbuf, 0 , sizeof(outbuf));
- @@ -1136,7 +1136,7 @@
- principal and point it to clients realm. This way
- we pass over a TGT of the clients realm. */
-
- - - sprintf(server_name,"host/%s@", remotehost);
- + sprintf(server_name,"host/%.100s@", remotehost);
- strncat(server_name,client->realm.data,client->realm.length);
- krb5_parse_name(ssh_context,server_name, &server);
- server->type = KRB5_NT_SRV_HST;
-
- However, there are also a few other potential problems in the Kerberos
- code. Special thanks to Barry Irwin <bvi@rucus.ru.ac.za> for pointing
- out one of them. I'm not yet sure whether they are exploitable (we
- don't have Kerberos installed here, nor documentation for it, so I
- can't easily check; however, they might be more serious than the
- above). I'd recommend everyone using ssh with Kerberos authentication
- compiled in to install the following full patch, just to make sure.
- In addition to the above, it explicitly restricts the length of
- various strings. This also includes the above.
-
- Tatu
-
- - --- sshconnect.c.orig Thu Nov 5 02:09:55 1998
- +++ sshconnect.c Thu Nov 5 02:10:53 1998
- @@ -282,7 +282,7 @@
-
- /* Child. Permanently give up superuser privileges. */
- if (setuid(getuid()) < 0)
- - - fatal("setuid: %s", strerror(errno));
- + fatal("setuid: %.100s", strerror(errno));
-
- /* Redirect stdin and stdout. */
- close(pin[1]);
- @@ -944,7 +944,7 @@
- if (!ssh_context)
- {
- if ((r = krb5_init_context(&ssh_context)))
- - - fatal("Kerberos V5: %s while initializing krb5.", error_message(r));
- + fatal("Kerberos V5: %.100s while initializing krb5.", error_message(r));
- krb5_init_ets(ssh_context);
- }
-
- @@ -959,14 +959,14 @@
- "host", KRB5_NT_SRV_HST,
- &creds.server)))
- {
- - - debug("Kerberos V5: error while constructing service name: %s.",
- + debug("Kerberos V5: error while constructing service name: %.100s.",
- error_message(r));
- goto cleanup;
- }
- if ((r = krb5_cc_get_principal(ssh_context, ccache,
- &creds.client)))
- {
- - - debug("Kerberos V5: failure on principal (%s).",
- + debug("Kerberos V5: failure on principal (%.100s).",
- error_message(r));
- goto cleanup;
- }
- @@ -975,7 +975,7 @@
- if ((r = krb5_get_credentials(ssh_context, 0,
- ccache, &creds, &new_creds)))
- {
- - - debug("Kerberos V5: failure on credentials(%s).",
- + debug("Kerberos V5: failure on credentials(%.100s).",
- error_message(r));
- goto cleanup;
- }
- @@ -987,7 +987,7 @@
- {
- if ((r = krb5_auth_con_init(ssh_context, &auth_context)))
- {
- - - debug("Kerberos V5: failed to init auth_context (%s)",
- + debug("Kerberos V5: failed to init auth_context (%.100s)",
- error_message(r));
- goto cleanup;
- }
- @@ -998,7 +998,7 @@
- if ((r = krb5_mk_req_extended(ssh_context, &auth_context, ap_opts,
- 0, new_creds, &auth)))
- {
- - - debug("Kerberos V5: failed krb5_mk_req_extended (%s)",
- + debug("Kerberos V5: failed krb5_mk_req_extended (%.100s)",
- error_message(r));
- goto cleanup;
- }
- @@ -1046,7 +1046,7 @@
-
- if (r = krb5_rd_rep(ssh_context, auth_context, &auth, &repl))
- {
- - - packet_disconnect("Kerberos V5 Authentication failed: %s",
- + packet_disconnect("Kerberos V5 Authentication failed: %.100s",
- error_message(r));
- goto cleanup;
- }
- @@ -1090,7 +1090,7 @@
- krb5_data outbuf;
- krb5_error_code r;
- int type;
- - - char server_name[128];
- + char server_name[512];
-
- remotehost = (char *) get_canonical_hostname();
- memset(&outbuf, 0 , sizeof(outbuf));
- @@ -1100,14 +1100,14 @@
- if (!ssh_context)
- {
- if ((r = krb5_init_context(&ssh_context)))
- - - fatal("Kerberos V5: %s while initializing krb5.", error_message(r));
- + fatal("Kerberos V5: %.100s while initializing krb5.", error_message(r));
- krb5_init_ets(ssh_context);
- }
- if (!auth_context)
- {
- if ((r = krb5_auth_con_init(ssh_context, &auth_context)))
- {
- - - debug("Kerberos V5: failed to init auth_context (%s)",
- + debug("Kerberos V5: failed to init auth_context (%.100s)",
- error_message(r));
- return 0 ;
- }
- @@ -1124,7 +1124,7 @@
- if ((r = krb5_cc_get_principal(ssh_context, ccache,
- &client)))
- {
- - - debug("Kerberos V5: failure on principal (%s)",
- + debug("Kerberos V5: failure on principal (%.100s)",
- error_message(r));
- return 0 ;
- }
- @@ -1136,7 +1136,7 @@
- principal and point it to clients realm. This way
- we pass over a TGT of the clients realm. */
-
- - - sprintf(server_name,"host/%s@", remotehost);
- + sprintf(server_name,"host/%.100s@", remotehost);
- strncat(server_name,client->realm.data,client->realm.length);
- krb5_parse_name(ssh_context,server_name, &server);
- server->type = KRB5_NT_SRV_HST;
- @@ -1145,7 +1145,7 @@
- if ((r = krb5_fwd_tgt_creds(ssh_context, auth_context, 0, client,
- server, ccache, 1, &outbuf)))
- {
- - - debug("Kerberos V5 krb5_fwd_tgt_creds failure (%s)",
- + debug("Kerberos V5 krb5_fwd_tgt_creds failure (%.100s)",
- error_message(r));
- krb5_free_principal(ssh_context, client);
- krb5_free_principal(ssh_context, server);
- @@ -1416,7 +1416,7 @@
- error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
- error("It is also possible that the host key has just been changed.");
- error("Please contact your system administrator.");
- - - error("Add correct host key in %s to get rid of this message.",
- + error("Add correct host key in %.100s to get rid of this message.",
- options->user_hostfile);
-
- /* If strict host key checking is in use, the user will have to edit
- @@ -1589,7 +1589,7 @@
- if (!ssh_context)
- {
- if ((problem = krb5_init_context(&ssh_context)))
- - - fatal("Kerberos V5: %s while initializing krb5.",
- + fatal("Kerberos V5: %.100s while initializing krb5.",
- error_message(problem));
- krb5_init_ets(ssh_context);
- }
- @@ -1605,7 +1605,7 @@
- if ((problem = krb5_cc_get_principal(ssh_context, ccache,
- &client)))
- {
- - - debug("Kerberos V5: failure on principal (%s).",
- + debug("Kerberos V5: failure on principal (%.100s).",
- error_message(problem));
- }
- else {
- - --- auth-kerberos.c.orig Thu Nov 5 02:06:02 1998
- +++ auth-kerberos.c Thu Nov 5 02:08:14 1998
- @@ -63,11 +63,11 @@
- krb5_auth_con_free(ssh_context, auth_context);
- auth_context = 0;
- }
- - - log_msg("Kerberos ticket authentication of user %s failed: %s",
- + log_msg("Kerberos ticket authentication of user %.100s failed: %.100s",
- server_user, error_message(problem));
-
- - - debug("Kerberos krb5_auth_con_genaddrs (%s).", error_message(problem));
- - - packet_send_debug("Kerberos krb5_auth_con_genaddrs: %s",
- + debug("Kerberos krb5_auth_con_genaddrs (%.100s).", error_message(problem));
- + packet_send_debug("Kerberos krb5_auth_con_genaddrs: %.100s",
- error_message(problem));
- return 0;
- }
- @@ -80,11 +80,11 @@
- krb5_auth_con_free(ssh_context, auth_context);
- auth_context = 0;
- }
- - - log_msg("Kerberos ticket authentication of user %s failed: %s",
- + log_msg("Kerberos ticket authentication of user %.100s failed: %.100s",
- server_user, error_message(problem));
-
- - - debug("Kerberos V5 rd_req failed (%s).", error_message(problem));
- - - packet_send_debug("Kerberos V5 krb5_rd_req: %s", error_message(problem));
- + debug("Kerberos V5 rd_req failed (%.100s).", error_message(problem));
- + packet_send_debug("Kerberos V5 krb5_rd_req: %.100s", error_message(problem));
- return 0;
- }
-
- @@ -93,22 +93,22 @@
- if (problem)
- {
- krb5_free_ticket(ssh_context, ticket);
- - - log_msg("Kerberos ticket authentication of user %s failed: %s",
- + log_msg("Kerberos ticket authentication of user %.100s failed: %.100s",
- server_user, error_message(problem));
-
- - - debug("Kerberos krb5_unparse_name failed (%s).", error_message(problem));
- - - packet_send_debug("Kerberos krb5_unparse_name: %s",
- + debug("Kerberos krb5_unparse_name failed (%.100s).", error_message(problem));
- + packet_send_debug("Kerberos krb5_unparse_name: %.100s",
- error_message(problem));
- return 0;
- }
- if (strncmp(server, "host/", strlen("host/")))
- {
- krb5_free_ticket(ssh_context, ticket);
- - - log_msg("Kerberos ticket authentication of user %s failed: invalid service name (%s)",
- + log_msg("Kerberos ticket authentication of user %.100s failed: invalid service name (%.100s)",
- server_user, server);
-
- - - debug("Kerberos invalid service name (%s).", server);
- - - packet_send_debug("Kerberos invalid service name (%s).", server);
- + debug("Kerberos invalid service name (%.100s).", server);
- + packet_send_debug("Kerberos invalid service name (%.100s).", server);
- krb5_xfree(server);
- return 0;
- }
- @@ -122,11 +122,11 @@
-
- if (problem)
- {
- - - log_msg("Kerberos ticket authentication of user %s failed: %s",
- + log_msg("Kerberos ticket authentication of user %.100s failed: %.100s",
- server_user, error_message(problem));
- - - debug("Kerberos krb5_copy_principal failed (%s).",
- + debug("Kerberos krb5_copy_principal failed (%.100s).",
- error_message(problem));
- - - packet_send_debug("Kerberos krb5_copy_principal: %s",
- + packet_send_debug("Kerberos krb5_copy_principal: %.100s",
- error_message(problem));
- return 0;
- }
- @@ -135,11 +135,11 @@
- /* Make the reply - so that mutual authentication can be done */
- if ((problem = krb5_mk_rep(ssh_context, auth_context, &reply)))
- {
- - - log_msg("Kerberos ticket authentication of user %s failed: %s",
- + log_msg("Kerberos ticket authentication of user %.100s failed: %.100s",
- server_user, error_message(problem));
- - - debug("Kerberos krb5_mk_rep failed (%s).",
- + debug("Kerberos krb5_mk_rep failed (%.100s).",
- error_message(problem));
- - - packet_send_debug("Kerberos krb5_mk_rep failed: %s",
- + packet_send_debug("Kerberos krb5_mk_rep failed: %.100s",
- error_message(problem));
- return 0;
- }
- @@ -160,7 +160,7 @@
- {
- krb5_creds **creds;
- krb5_error_code retval;
- - - static char ccname[128];
- + static char ccname[512];
- krb5_ccache ccache = NULL;
- struct passwd *pwd;
- extern char *ticket;
- @@ -208,9 +208,9 @@
-
- if (retval = krb5_rd_cred(ssh_context, auth_context, krb5data, &creds, NULL))
- {
- - - log_msg("Kerberos V5 tgt rejected for user %.100s : %s", server_user,
- + log_msg("Kerberos V5 tgt rejected for user %.100s : %.100s", server_user,
- error_message(retval));
- - - packet_send_debug("Kerberos V5 tgt rejected for %.100s : %s",
- + packet_send_debug("Kerberos V5 tgt rejected for %.100s : %.100s",
- server_user,
- error_message(retval));
- packet_start(SSH_SMSG_FAILURE);
- @@ -234,7 +234,7 @@
- goto errout;
-
- ticket = xmalloc(strlen(ccname) + 1);
- - - (void) sprintf(ticket, "%s", ccname);
- + (void) sprintf(ticket, "%.100s", ccname);
-
- /* Successful */
- packet_start(SSH_SMSG_SUCCESS);
- @@ -244,9 +244,9 @@
-
- errout:
- krb5_free_tgt_creds(ssh_context, creds);
- - - log_msg("Kerberos V5 tgt rejected for user %.100s :%s", server_user,
- + log_msg("Kerberos V5 tgt rejected for user %.100s :%.100s", server_user,
- error_message(retval));
- - - packet_send_debug("Kerberos V5 tgt rejected for %.100s : %s", server_user,
- + packet_send_debug("Kerberos V5 tgt rejected for %.100s : %.100s", server_user,
- error_message(retval));
- packet_start(SSH_SMSG_FAILURE);
- packet_send();
-
-
- - --
- SSH Communications Security http://www.ssh.fi/
- SSH IPSEC Toolkit http://www.ipsec.com/
- Free Unix SSH http://www.ssh.fi/sshprotocols2/
-
- -----BEGIN PGP SIGNATURE-----
- Version: 2.6.3i
- Charset: noconv
-
- iQCVAwUBNkDyOakZxfGWH0o1AQGYOQP/bUNnE/ZpSQqWVc0ngxLG50+CtyksugLJ
- wD0X2yIoc8jmY+UNPL7weQatgv6CmUUoWWpLctzKr8A6G/HrD2sh0OHPBwhIxg1i
- 3mPj7WrcIX9g/K5LaEksiZ0vv4h/gvSJty5y+wRiu0QLRmuAy91CyaKTV7Sab0YT
- /W/s1NazNIg=
- =iABB
- -----END PGP SIGNATURE-----
-
- ----------------------------------------------------------------
-
- Date: Wed, 4 Nov 1998 13:50:49 +0100
- From: Joel Eriksson <na98jen@STUDENT.HIG.SE>
- To: BUGTRAQ@netspace.org
- Subject: Re: ssh-1.2.26 buffer overflow patch
-
- On Sun, 1 Nov 1998, Andy Church wrote:
-
- > [login.c:538]
- > log_msg("putuserattr S_LASTTTY %.900s failed: %.100s", /*...*/);
- >
- > [log-server.c:130]
- > void log_msg(const char *fmt, ...)
- > {
- > char buf[1024];
- > /*...*/
- > vsprintf(buf, fmt, args);
- > /*...*/
- > }
- >
- > Granted, I haven't checked whether this is exploitable, but I could never
- > call that secure. (Count the bytes.)
-
- I don't think there's any reason of not using snprintf, but, in this
- particular case they seem to be rather well protected anyway. I have
- noticed the line you mention above, and a few others that woke my
- interest. But since the whole log_msg-call reads:
-
- log_msg("putuserattr S_LASTTTY %.900s failed: %.100s",
- ttyname, strerror(errno));
-
- It means that strerror() must return a string with a length around 100
- bytes *and* ttyname should be around 900 bytes, I have not checked if that
- is possible, but I certainly don't think so.
-
- There are a few other places that made me wonder, like when lines like:
-
- log_msg("Connection for %.200s not allowed from %s\n",
- user, get_canonical_hostname());
-
- were used, when tracing back to get_canonical_hostname() and then back to
- get_remote_hostname() it seems as the name is limited to 255 bytes anyway.
-
- In auth-kerberos.c there are frequent use of %s in the log_msg()-calls and
- it sure looks dangerous.. In the places where I have tracked the origin of
- the strings supplied their length have been checked *before* log_msg() is
- used. I think that IBM's statements about not having found any exploitable
- overflows in ssh-1.2.26 can probably be trusted, but since the functions
- for logging uses vsprintf to a buffer on 1024 bytes it certainly is very
- possible to introduce a severe securityhole if not being careful. It
- certainly seems a lot easier to me to use vsnprintf's instead and limit
- the length to 1023 bytes instead of having to track back every use of the
- functions to be certain no securityproblem exists.
-
- > --Andy Church | If Bell Atlantic really is the heart
- > achurch@dragonfire.net | of communication, then it desperately
- > http://achurch.dragonfire.net/ | needs a quadruple bypass.
-
- /Joel Eriksson
-